iT邦幫忙

2025 iThome 鐵人賽

DAY 8
0

前情提要

  • 前面準備了 API Gateway 拿來作為 Serverless API 的入口,就是用一個便宜的服務來顧門
  • 有人上門的時候,就過濾他想要做什麼
    • 現在我們提供他去產生 影片S3 presigned url
  • 頁面要放在前一回 前端S3 的 Bucket
  • 因為我們還沒講到 CDN / CloudFront,這篇文章會先用一個『不是最佳實踐』的做法來搞;就是先取消 exsky-vlog-web Bucket 的 Block Public Access

原始碼

index.html

<!DOCTYPE html>
<html lang="zh-TW">
<head>
  <meta charset="UTF-8">
  <title>影片上傳平台</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="upload-container">
    <h2>上傳影片</h2>
    <form id="uploadForm">
      <input type="file" id="fileInput" accept="video/*" required><br>
      <button type="submit">開始上傳</button>
    </form>
    <div class="status" id="status"></div>
  </div>

  <script src="script.js"></script>
</body>
</html>

style.css

body {
  font-family: Arial, sans-serif;
  background: linear-gradient(135deg, #eef2f3, #8e9eab);
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 0;
}

.upload-container {
  background: #fff;
  padding: 2rem;
  border-radius: 12px;
  box-shadow: 0 8px 20px rgba(0,0,0,0.15);
  width: 400px;
  text-align: center;
}

h2 {
  margin-bottom: 1rem;
  color: #333;
}

input[type="file"] {
  margin: 1rem 0;
  padding: 0.5rem;
}

button {
  background-color: #4CAF50;
  color: white;
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 1rem;
  transition: 0.3s;
}

button:hover {
  background-color: #45a049;
}

.status {
  margin-top: 1rem;
  color: #555;
}

script.js

const form = document.getElementById('uploadForm');
const fileInput = document.getElementById('fileInput');
const statusDiv = document.getElementById('status');

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const file = fileInput.files[0];
  if (!file) {
    statusDiv.textContent = "請先選擇影片!";
    return;
  }

  statusDiv.textContent = "正在請求上傳網址...";

  try {
    // ⚠️ 請換成你的 API Gateway endpoint
    const res = await fetch("https://iwlw3i3ys4.execute-api.ap-northeast-1.amazonaws.com/prod/generate-url", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ filename: file.name })
    });
    const data = await res.json();
    const uploadUrl = data.url;

    statusDiv.textContent = "正在上傳影片...";

    // PUT 檔案到 S3 (Presigned URL)
    await fetch(uploadUrl, {
      method: "PUT",
      body: file
    });

    statusDiv.textContent = "✅ 上傳完成!";
  } catch (err) {
    console.error(err);
    statusDiv.textContent = "❌ 上傳失敗,請稍後再試";
  }
});

設定

  1. 確認 S3 Bucket 取消阻擋公開訪問
  2. 允許 API Gateway 的 CORS 設定
    https://ithelp.ithome.com.tw/upload/images/20250919/20130149c5TumIBXks.png
  3. Lambda Function 的 Code 要修改一下
    import json
    import boto3
    import base64
    import os
    
    s3 = boto3.client("s3")
    BUCKET_NAME = os.environ.get("BUCKET_NAME", "my-upload-bucket")
    
    def lambda_handler(event, context):
        try:
            # API Gateway 預設會把 binary 轉成 base64
            file_content = base64.b64decode(event["body"])
    
            # 從 query string 或 header 取檔名
            filename = event.get("queryStringParameters", {}).get("filename", "upload.bin")
    
            # 上傳到 S3
            s3.put_object(
                Bucket=BUCKET_NAME,
                Key=filename,
                Body=file_content,
                ContentType="video/mp4"  # 可依需求調整
            )
    
            return {
                "statusCode": 200,
                "headers": {
                    "Access-Control-Allow-Origin": "*",
                    "Access-Control-Allow-Methods": "OPTIONS,POST",
                    "Access-Control-Allow-Headers": "Content-Type"
                },
                "body": json.dumps({"message": f"檔案 {filename} 已上傳成功"})
            }
        except Exception as e:
            return {
                "statusCode": 500,
                "body": json.dumps({"error": str(e)})
            }
    
    

}
```
https://ithelp.ithome.com.tw/upload/images/20250919/201301492bDmThejMR.png
4. 改完之後要將 Lambda 新版本 Deploy 上去...見上圖右上角的下拉選單...
5. 從 http://exsky-vlog-web.s3-website-ap-northeast-1.amazonaws.com/ 這個網址去取用 S3 Bucket 的靜態網頁、上傳頁面
6. 觀看上傳結果
https://ithelp.ithome.com.tw/upload/images/20250919/201301499CcTBs8wOr.png

結論

  • CORS 這個機制是為了避免釣魚行為,在 A網站 對 B網站進行 API 呼叫
  • 沒有 Block Public Access 會有很多問題,雖然有透過 Bucket Policy 去縮小允許的操作動作,僅可 GetObjects,但也不能避免有心人士瘋狂來撈網頁...

上一篇
【Day 7】 用 Amazon S3 架靜態網站
下一篇
【Day 9】 關掉 BPA 的 S3 將會面臨什麼議題? / 好 CDN 不拿來用嗎?
系列文
無法成為片師也想拍 Vlog?!個人影音小工具的誕生!9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言